﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Data;
using System.Collections;
using System.Collections.ObjectModel;

namespace Wimat.Collections
{
    public class MultiSplitCollectionView<TSource1, TSource2, TSource3, TResult> : IMultiSplitCollectionView
    {
        #region Source1
        private ReadOnlyObservableCollection<TSource1> _Domain1;
        public ReadOnlyObservableCollection<TSource1> Domain1
        {
            get { return _Domain1; }
            set
            {
                _Domain1 = value;
                _Source1 = new ListCollectionView(_Domain1);
                _Source1.Filter = (x) =>
                {
                    return x == null
                        || ((Domain1Filter == null || Domain1Filter((TSource1)x))
                        && (Source1ContainedInResults == null || !Source1ContainedInResults((TSource1)x, _resultsReadOnly)));
                };
            }
        }
        private Predicate<TSource1> _Domain1Filter;
        public Predicate<TSource1> Domain1Filter
        {
            get { return _Domain1Filter; }
            set
            {
                _Domain1Filter = value;
                if (_Source1 != null)
                {
                    _Source1.Refresh();
                }
            }
        }

        private ListCollectionView _Source1;

        public ListCollectionView Source1
        {
            get
            {
                return _Source1;
            }
        }
        public Func<TSource1, TResult> Source1ToResult
        {
            get;
            set;
        }
        private Func<TSource1, ReadOnlyCollection<TResult>, bool> _Source1ContainedInResults;
        public Func<TSource1, ReadOnlyCollection<TResult>, bool> Source1ContainedInResults
        {
            get { return _Source1ContainedInResults; }
            set
            {
                _Source1ContainedInResults = value;
                if (_Source1 != null)
                {
                    _Source1.Refresh();
                }
            }
        }
        public void MoveCurrentSource1ToResults()
        {
            if (_Source1.CurrentItem == null) return;
            var c = (TSource1)_Source1.CurrentItem;
            _results.Add(Source1ToResult(c));
            _ResultView.Refresh();
            _Source1.Refresh();
        }
        #endregion
        #region Source2
        private ReadOnlyObservableCollection<TSource2> _Domain2;
        public ReadOnlyObservableCollection<TSource2> Domain2
        {
            get { return _Domain2; }
            set
            {
                _Domain2 = value;
                _Source2 = new ListCollectionView(_Domain2);
                _Source2.Filter = (x) =>
                {
                    return x == null
                        || ((Domain2Filter == null || Domain2Filter((TSource2)x))
                        && (Source2ContainedInResults == null || !Source2ContainedInResults((TSource2)x, _resultsReadOnly)));
                };
            }
        }
        private Predicate<TSource2> _Domain2Filter;
        public Predicate<TSource2> Domain2Filter
        {
            get { return _Domain2Filter; }
            set
            {
                _Domain2Filter = value;
                if (_Source2 != null)
                {
                    _Source2.Refresh();
                }
            }
        }

        private ListCollectionView _Source2;

        public ListCollectionView Source2
        {
            get
            {
                return _Source2;
            }
        }
        public Func<TSource2, TResult> Source2ToResult
        {
            get;
            set;
        }
        private Func<TSource2, ReadOnlyCollection<TResult>, bool> _Source2ContainedInResults;
        public Func<TSource2, ReadOnlyCollection<TResult>, bool> Source2ContainedInResults
        {
            get { return _Source2ContainedInResults; }
            set
            {
                _Source2ContainedInResults = value;
                if (_Source2 != null)
                {
                    _Source2.Refresh();
                }
            }
        }
        public void MoveCurrentSource2ToResults()
        {
            if (_Source2.CurrentItem == null) return;
            var c = (TSource2)_Source2.CurrentItem;
            _results.Add(Source2ToResult(c));
            _ResultView.Refresh();
            _Source2.Refresh();
        }
        #endregion
        #region Source3
        private ReadOnlyObservableCollection<TSource3> _Domain3;
        public ReadOnlyObservableCollection<TSource3> Domain3
        {
            get { return _Domain3; }
            set
            {
                _Domain3 = value;
                _Source3 = new ListCollectionView(_Domain3);
                _Source3.Filter = (x) =>
                {
                    return x == null
                        || ((Domain3Filter == null || Domain3Filter((TSource3)x))
                        && (Source3ContainedInResults == null || !Source3ContainedInResults((TSource3)x, _resultsReadOnly)));
                };
            }
        }
        private Predicate<TSource3> _Domain3Filter;
        public Predicate<TSource3> Domain3Filter
        {
            get { return _Domain3Filter; }
            set
            {
                _Domain3Filter = value;
                if (_Source3 != null)
                {
                    _Source3.Refresh();
                }
            }
        }

        private ListCollectionView _Source3;

        public ListCollectionView Source3
        {
            get
            {
                return _Source3;
            }
        }
        public Func<TSource3, TResult> Source3ToResult
        {
            get;
            set;
        }
        private Func<TSource3, ReadOnlyCollection<TResult>, bool> _Source3ContainedInResults;
        public Func<TSource3, ReadOnlyCollection<TResult>, bool> Source3ContainedInResults
        {
            get { return _Source3ContainedInResults; }
            set
            {
                _Source3ContainedInResults = value;
                if (_Source3 != null)
                {
                    _Source3.Refresh();
                }
            }
        }

        public void MoveCurrentSource3ToResults()
        {
            if (_Source3.CurrentItem == null) return;
            var c = (TSource3)_Source3.CurrentItem;
            _results.Add(Source3ToResult(c));
            _ResultView.Refresh();
            _Source3.Refresh();
        }
        #endregion

        #region Results
        private ObservableCollection<TResult> _results;
        private ReadOnlyCollection<TResult> _resultsReadOnly;
        private ListCollectionView _ResultView;
        public ListCollectionView Result
        {
            get
            {
                return _ResultView;
            }
        }

        private void InitResultsIfNull()
        {
            if (_results == null)
            {
                _results = new ObservableCollection<TResult>();
                _resultsReadOnly = new ReadOnlyCollection<TResult>(_results);
                _ResultView = new ListCollectionView(_resultsReadOnly);
                _RemovedResults = new ObservableCollection<TResult>();
                _RemovedResultsReadOnly = new ReadOnlyObservableCollection<TResult>(_RemovedResults);
            }
        }
        public IEnumerable<TResult> OriginalResults
        {
            set
            {
                InitResultsIfNull();
                foreach (var t in value)
                {
                    _results.Add(t);
                }
            }
        }
        private ObservableCollection<TResult> _RemovedResults;
        private ReadOnlyObservableCollection<TResult> _RemovedResultsReadOnly;
        public ReadOnlyObservableCollection<TResult> RemovedResults
        {
            get
            {
                InitResultsIfNull();
                return _RemovedResultsReadOnly;
            }
        }
        #endregion

        public Action<TResult> OnResultRemoved
        {
            get;
            set;
        }
        public void RemoveCurrentFromResults()
        {
            if (_ResultView.CurrentItem == null) return;

            var c = (TResult)_ResultView.CurrentItem;
            RemoveFromResults(c);
        }
        public void RemoveFromResults(TResult r)
        {
            bool succeeded = _results.Remove(r);
            if (!succeeded) throw new ArgumentException();

            _RemovedResults.Add(r);
            if (OnResultRemoved != null)
            {
                OnResultRemoved(r);
            }
            _ResultView.Refresh();
            _Source1.Refresh();
            _Source2.Refresh();
            _Source3.Refresh();
        }

        public MultiSplitCollectionView()
        {
            InitResultsIfNull();
        }
    }
}
